﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;

using log4net;
using ConfigUtilLib;
using QueueMgrUtil;
using CommonLib;


namespace DataFetcherProcessorLib
{
    public class DataProcessManager
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private static AppConfig AppConfig { get; set; }
        private static CountdownEvent _countdown = null; // Static due to Thread Use in the same class.
        private const string InterfaceStr = "CommonLib.ProcessInterface";
        private const string ReportSummaryInterfaceStr = "CommonLib.ReportSummaryIntf";

        /// <summary>
        /// 
        /// </summary>
        private DataProcessManager()
        {

        }

        #region PUBLIC_METHODS

        public static void KickOffProcessingData(AppConfig appCfg)
        {
            AppConfig = appCfg;
            ProcessQueuedRequests();
        }
        #endregion

        #region Private Method
        /// <summary>
        /// Processing Queue Requests .. 
        /// </summary>
        private static void ProcessQueuedRequests()
        {
            log.DebugFormat("Starting Processing Thread");

            BlockReadQueue<QueueWorkItem> workItemsQueue = QueueManager.WorkItemsQueue;
            _countdown = null;

            while (true)
            {

                QueueWorkItem workItem = workItemsQueue.Dequeue();

                if (workItem != null)
                {
                    if (workItem.cmdType == QUEUE_WORK_ITEM_TYPE.PROCESS_END)
                    {
                        log.DebugFormat(" Completed  Processing All Work Items .. Waiting For Threads to finish ..");
                        if (_countdown != null)
                            _countdown.Wait();

                        // Process Close up Actions from all data processors .. 
                        log.DebugFormat("  .. Processing Threads Done Their Job, Let us Kick off End Game ");
                        ProcessEndOfTheGame();


                        return;
                    }

                    if (_countdown == null)
                    {
                        _countdown = new CountdownEvent(1);
                    }
                    else
                    {
                        if (_countdown.CurrentCount == 0)
                        {
                            _countdown = new CountdownEvent(1);
                        }
                        else
                        {
                            _countdown.AddCount();
                        }
                    }

                    //  Let Us spin off a thread to execute this. 
                    ThreadPool.QueueUserWorkItem(ProcessQueuedWorkItemThreadFunction, workItem);

                }
            }

        }

        // Thread
        private static void ProcessQueuedWorkItemThreadFunction(Object args)
        {
            var workItem = args as QueueWorkItem;
            //workItem.ProcessRawData(_appConfig);            


            var fetchRec = workItem.ConfigRecord as FetcherConfigBaseRec;
            if (fetchRec == null)
            {
                log.DebugFormat("Config Record Missing for QueueItem {0}", workItem.uniqueID.ToString());
                if (_countdown != null)
                {
                    _countdown.Signal();
                }
                return;
            }

            object rawData = workItem.ResultRecord;

            if (rawData == null)
            {
                log.DebugFormat("Raw Carwled Data  Record Missing for QueueItem {0}", workItem.uniqueID.ToString());
                if (_countdown != null)
                {
                    _countdown.Signal();
                }
                return;
            }

            // Super Cool Process Now .. 

            log.DebugFormat(" Processing Jobs for : {0}", fetchRec.FetcherName);

            List<ProcessorRecord> procRecs = AppConfig.getProcesserListAssociatedWithFetcher(fetchRec.FetcherName);

            if (procRecs == null)
            {
                log.WarnFormat(" Processers Missing for Fetcher : {0}", fetchRec.FetcherName);
                if (_countdown != null)
                {
                    _countdown.Signal();
                }
                return;
            }
            // Loop thru Everey Process Object 
            foreach (ProcessorRecord processObject in procRecs)
            {
                Assembly reflectionAssembly = null;
                string asmFullPath = processObject.AssemblyDllName;
                try
                {
                    // 1. Load Library 
                    reflectionAssembly = Assembly.LoadFrom(asmFullPath);
                }
                catch (Exception e)
                {
                    log.DebugFormat("Unable to Load DLL : {0} : {1}", asmFullPath, e.Message);
                    continue;
                }

                if (reflectionAssembly == null) continue;

                foreach (Type classType in reflectionAssembly.GetTypes())
                {

                    // 2. Verify Credentials  

                    if ((classType == null) || (classType.IsClass != true) || (classType.GetInterface(InterfaceStr) == null))
                    {
                        continue;
                    }

                    // if all good Invoke The method 
                    var ibaseObject = Activator.CreateInstance(classType) as ProcessInterface;
                    log.DebugFormat("......Dynamically Invoking compute() on {0} Class of {1}", classType.FullName, workItem.uniqueID.ToString());

                    //string configPath = processObject.ProcessConfigFile;
                    // Kick it off in a Thread.. 
                    if (ibaseObject != null)
                        ibaseObject.processrawData(fetchRec.FetcherName, rawData, processObject.ProcessorConfig);
                    log.DebugFormat("Processing :  {0} @ ID  {1} ", fetchRec.FetcherName, workItem.uniqueID.ToString());

                }
            }

            if (_countdown != null)
            {
                _countdown.Signal();
            }

        }

        private static void ProcessEndOfTheGame()
        {
            log.DebugFormat(" Processing End Game  Now for every configured active processor ...");

            foreach (FetcherConfigBaseRec fetcherRec in AppConfig.FetcherRecords)
            {
                var procRecs = AppConfig.getProcesserListAssociatedWithFetcher(fetcherRec.FetcherName);

                if (procRecs == null)
                {
                    log.WarnFormat(" Processers Missing for Fetcher : {0}", fetcherRec.FetcherName);
                    continue;
                }

                log.DebugFormat(" Processing End game for Processors associated with Fetcher : {0}", fetcherRec.FetcherName);


                foreach (ProcessorRecord processObject in procRecs)
                {
                    Assembly reflectionAssembly = null;
                    string asmFullPath = processObject.AssemblyDllName;
                    try
                    {
                        // 1. Load Library 
                        reflectionAssembly = Assembly.LoadFrom(asmFullPath);
                    }
                    catch (Exception e)
                    {
                        log.DebugFormat("End Game :  Unable to Load DLL : {0} : {1}", asmFullPath, e.Message);
                        continue;
                    }

                    if (reflectionAssembly == null) continue;

                    foreach (Type classType in reflectionAssembly.GetTypes())
                    {

                        // 2. Verify Credentials  

                        if ((classType == null) || (classType.IsClass != true) || (classType.GetInterface(ReportSummaryInterfaceStr) == null))
                        {
                            continue;
                        }

                        // if all good Invoke The method 
                        var ibaseObject = Activator.CreateInstance(classType) as ReportSummaryIntf;
                        log.DebugFormat("End game ......Dynamically Invoking compute() on {0} ", classType.FullName);

                        string configPath = processObject.ProcessConfigFile;
                        // Kick it off in a Thread.. 
                        if (ibaseObject != null) ibaseObject.reportSummary();
                    }

                }



            }




        }

        #endregion
    }
}
